package com.duobaoyu.dby.supply.biz.strategy.message.refund;

import com.duobaoyu.dby.channel.adapter.bean.message.refund.AfterSaleStatusMessage;
import com.duobaoyu.dby.channel.adapter.bean.refund.AfterSaleOrderInfo;
import com.duobaoyu.dby.channel.adapter.enums.ChannelCodeEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.refund.AfterSaleStatusEnum;
import com.duobaoyu.dby.channel.boundary.bean.refund.result.AfterSaleParticularResult;
import com.duobaoyu.dby.channel.boundary.bean.refund.result.AfterSaleRefundDetailResult;
import com.duobaoyu.dby.common.mybatis.util.EnumUtils;
import com.duobaoyu.dby.supply.biz.constant.OperateSourceConstants;
import com.duobaoyu.dby.supply.biz.service.order.OrderService;
import com.duobaoyu.dby.supply.common.biz.bo.refund.AfterSaleOutBO;
import com.duobaoyu.dby.supply.common.biz.bo.refund.AfterSaleStateContext;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by shuming on 2023/10/13 09:30
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class JdVopChannelAfterSaleStatusAsyncHandler extends AbstractChannelAfterSaleStateChangeHandler {

    private final OrderService orderService;

    @Override
    protected Set<String> channelCodes() {
        return Sets.newHashSet(ChannelCodeEnum.JD_VOP.getValue());
    }

    @Override
    protected Void handler(AfterSaleStateChangeMessageAsyncHandler.Context context,
                           Object beforeResult) {
        handleRefundSuccessChannelMessage(context);
        handleNoRefundSuccessChannelMessage(context);
        return null;
    }

    private void handleRefundSuccessChannelMessage(AfterSaleStateChangeMessageAsyncHandler.Context context) {

        List<AfterSaleStatusMessage> channelMessages = context.getOriginMessages();
        List<AfterSaleStatusMessage> refundAfterSaleMessages = channelMessages.stream()
                .filter(e -> Objects.nonNull(e.getRefundInfo())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(refundAfterSaleMessages)) {
            return;
        }
        Map<String, AfterSaleOutBO> channelAfterSaleSnMap = context.getAfterSales().stream()
                .collect(Collectors.toMap(AfterSaleOutBO::getChannelAfterSaleSn, Function.identity()));

        List<AfterSaleStatusMessage> handleFailureMessages = Lists.newArrayList();
        List<AfterSaleStateContext> stateChanges = refundAfterSaleMessages.stream()
                .map(e -> {
                    // 获取售后退款详情
                    try {
                        AfterSaleOutBO afterSale = channelAfterSaleSnMap.get(e.getChannelAfterSaleSn());
                        AfterSaleRefundDetailResult refundResult = afterSaleBoundaryClient
                                .jdVopAfterSaleRefundDetail(afterSale);
                        List<AfterSaleStateContext.AfterSaleUpdateItemInfo> afterSaleItemInfoList = buildRefundInfo(refundResult.getChannelRefundAmount(),
                                null, afterSale);
                        // 退款
                        BigDecimal channelActualRefundAmount = null,
                                channelFreightActualRefundAmount = null;

                        if (Objects.nonNull(refundResult.getChannelRefundAmount())) {
                            channelActualRefundAmount = afterSaleItemInfoList.stream().map(AfterSaleStateContext.AfterSaleUpdateItemInfo::getChannelActualRefundAmount)
                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                            channelFreightActualRefundAmount = afterSaleItemInfoList.stream().map(AfterSaleStateContext.AfterSaleUpdateItemInfo::getChannelFreightActualRefundAmount)
                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                        }
                        AfterSaleStateContext afterSaleContext = new AfterSaleStateContext();
                        afterSaleContext.setAfterSale(afterSale);
                        afterSaleContext.setCurrentAfterSaleStatus(AfterSaleStatusEnum.TO_BE_CONFIRMED_BY_USER.getValue());
                        afterSaleContext.setOperateSource(OperateSourceConstants.OPERATION_CHANNEL);
                        afterSaleContext.setChangeContentOperate(AfterSaleStateContext.ChangeContentOperate.builder()
                                .afterSaleItemInfoList(afterSaleItemInfoList)
                                .channelActualRefundAmount(channelActualRefundAmount)
                                .channelFreightActualRefundAmount(channelFreightActualRefundAmount)
                                .build());
                        return afterSaleContext;
                    } catch (Exception ex) {
                        log.warn("invoke jdVop afterSaleParticular failed, param:{}", e.getChannelAfterSaleSn(), ex);
                        handleFailureMessages.add(e);
                        return null;
                    }
                }).filter(Objects::nonNull)
                .collect(Collectors.toList());
        context.getHandleFailureMessages().addAll(handleFailureMessages);
        context.getHandleSuccessMessages().addAll(stateChanges);
    }

    private void handleNoRefundSuccessChannelMessage(AfterSaleStateChangeMessageAsyncHandler.Context context) {
        Map<String, AfterSaleOutBO> channelAfterSaleSnMap = context.getAfterSales().stream()
                .collect(Collectors.toMap(AfterSaleOutBO::getChannelAfterSaleSn, Function.identity()));

        List<AfterSaleStatusMessage> handleFailureMessages = Lists.newArrayList();
        List<AfterSaleStateContext> contexts = context.getOriginMessages().stream()
                .filter(e -> Objects.isNull(e.getRefundInfo()))
                .map(message -> {
                    try {
                        AfterSaleParticularResult result = afterSaleBoundaryClient.queryAfterSale(channelAfterSaleSnMap.get(message.getChannelAfterSaleSn()));
                        AfterSaleOrderInfo afterSaleOrderInfo = result.getAfterSaleOrderInfo();
                        AfterSaleStateContext stateChange = new AfterSaleStateContext();
                        stateChange.setAfterSale(channelAfterSaleSnMap.get(message.getChannelAfterSaleSn()));
                        stateChange.setCurrentAfterSaleStatus(getCurrentAfterSaleStatus(channelAfterSaleSnMap.get(message.getChannelAfterSaleSn()), message, afterSaleOrderInfo));
                        stateChange.setChangeContentOperate(AfterSaleStateContext.ChangeContentOperate.builder()
                                .newPickupType(afterSaleOrderInfo.getNewPickupType())
                                .failReason(afterSaleOrderInfo.getRejectReason())
                                .build());
                        return stateChange;
                    } catch (Exception ex) {
                        log.warn("invoke jdVop afterSaleParticular failed, param:{}", message.getChannelAfterSaleSn());
                        handleFailureMessages.add(message);
                        return null;
                    }
                }).filter(Objects::nonNull)
                .collect(Collectors.toList());
        context.getHandleFailureMessages().addAll(handleFailureMessages);
        context.getHandleSuccessMessages().addAll(contexts);
    }

    private static Integer getCurrentAfterSaleStatus(AfterSaleOutBO afterSale,
                                                     AfterSaleStatusMessage e,
                                                     AfterSaleOrderInfo afterSaleOrderInfo) {
        Integer status = Optional.ofNullable(afterSaleOrderInfo.getAfterSaleStatus())
                .orElse(e.getAfterSaleStatus());
        if (EnumUtils.eq(AfterSaleStatusEnum.COMPLETED,status)
                && EnumUtils.eq(AfterSaleStatusEnum.TO_BE_REVIEWED_BY_CHANNEL,afterSale.getStatus())) {
            return AfterSaleStatusEnum.WAIT_REJECT_CONFIRM.getValue();
        } else {
            return status;
        }
    }
}
